package core;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

import util.Debug;
import net.Connector;
import net.Daemon;

/**
 * Spawn a servent with a daemon port
 * 
 * @author Emil Wall
 */
public class Servent extends Thread {

	private static final int SLEEP_MS = 200;
//	private LinkedList<Connector> connectors;
	private Hashtable<String, Connector> connectors;
	private Daemon daemon;
	private final int myPort;
	private int initPort;
	private String name;
	private Frame master;
	private boolean running = true;
	
	public String lastMsgFrom = null;
	
	/**
	 * Allows other threads to discontinue execution of this thread
	 * @param run false to stop execution in orderly manner.
	 */
	public void setRunning(boolean run) {
		synchronized(this) {
			running = run;
		}
		
		if(!running) {
			// Clean up daemon and connector threads
			daemon.cleanup();
			synchronized(this) {
				Enumeration<Connector> iter = connectors.elements();
				while(iter.hasMoreElements()) {
					Connector temp = iter.nextElement();
					temp.shouldRun(false);
				}
			}
			connectors.clear();
		}
	}
	
	/**
	 * Get method for running status
	 * @return true if Servent is (supposed to be) still running, else false
	 */
	public synchronized boolean getRunning() {
		return running;
	}
	
	/**
	 * Spawns a new Servent Node
	 * @param listen_port to listen on
	 * @param init_port to connect to
	 * @param name of the servent
	 * @param master JFrame containing servent
	 */
	public Servent(int listen_port, int init_port, String name, Frame master) {
		this.name = name;
		myPort = listen_port;
		initPort = init_port;
		this.master = master;
		connectors = new Hashtable<String, Connector>();
	}
	
	/**
	 * run method. Spawns daemon and init connector.
	 */
	public void run() {
		master.writeToLog(name, "SERVENT thread started...");
		
		// Daemon
		try {
			daemon = new Daemon(myPort,name, master);
		} catch (IOException e) {
			if(Debug.on) {
				master.writeToLog(name, "Error: Daemon port already in use.");
				master.writeToLog(name, "SERVENT thread terminating...");
			}
//			if(Debug.verbose) {
//				e.printStackTrace();
//			}
			master.stop();
			return;
		}
		daemon.start();
		
		// Connector
		connectTo(null, initPort);
		
		// Periodically sleep until terminated
		while(getRunning()) {
			try {
//				master.writeToLog(name, "Main thread going to sleep for "+SLEEP_MS+" ms...");
				Thread.sleep(SLEEP_MS);
			} catch (InterruptedException e) {
				if(Debug.verbose) {
					e.printStackTrace();
				}
			}
		}
		master.writeToLog(name, "SERVENT thread terminating...");
	}
	
	/**
	 * Spawns a new connection to another node.
	 * @param ip String representation of ip adress to connect to, 
	 * or null if localhost
	 * @param port remote Daemon port
	 */
	public void connectTo(String ip, int port) {
		if(getRunning()) {
			Connector conn = new Connector(ip, port, name, master);
			synchronized(this) {
				connectors.put(ip+":"+port, conn);
			}
			conn.start();
		} else if(Debug.on) {
			master.writeToLog(name, "Servent not running. " +
					"Ignoring connection request.");
		}
	}
	
	/**
	 * Terminates an existing connection to another node.
	 * @param ip String representation of ip adress to disconnect from, 
	 * or null if localhost
	 * @param port remote Daemon port
	 */
	public void disconnectFrom(String ip, int port) {
		if(ip == null) {
			ip = "localhost";
		}
		if(getRunning()) {
			Connector conn = null;
			synchronized(this) {
				conn = connectors.remove(ip+":"+port);
			}
			conn.shouldRun(false);
		} else if(Debug.on) {
			master.writeToLog(name, "Servent not running. " +
					"Ignoring disconnect request.");
		}
	}

}
